#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <cstdio>
#include <cstring>
#include <cstdbool>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>

#include "err.hpp"

namespace ns_server
{
    static const int backlog = 32; // 服务器套接字能够同时处理的、还未被accept函数读取的连接请求的最大数量为32
    static const uint16_t default_port = 8081;
    using func_t = std::function<std::string(const std::string &)>;

    class TcpServer; // 前置声明
    // 线程数据
    class ThreadData
    {
    public:
        ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *ts)
            :sock(fd), clientip(ip), clientport(port), current(ts)
        {}

        ~ThreadData() {}

    public:
        int sock;
        std::string clientip;
        uint16_t clientport;
        TcpServer *current; // 用于线程的传参调用service成员函数
    };
    // 服务器
    class TcpServer
    {
    public:
        TcpServer(func_t func, uint16_t port = default_port)
            : port_(port),
              quit_(true),
              func_(func)
        {
        }

        void ServerInit()
        {
            // 创建套接字
            listensock_ = socket(AF_INET, SOCK_STREAM, 0); // 使用IPV4、TCP协议
            if (listensock_ < 0)
            {
                std::cerr << "create socket error" << std::endl;
                exit(SOCKET_ERR);
            }

            // bind绑定
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local)); // 清零

            local.sin_family = AF_INET;
            local.sin_port = htons(port_);
            local.sin_addr.s_addr = htons(INADDR_ANY); // 0.0.0.0任意的网络IP都可以接收

            if (bind(listensock_, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                // 绑定失败
                std::cerr << "bind socket error" << std::endl;
                exit(BIND_ERR);
            }

            // 与udp不同的是，tcp需要监听
            if (listen(listensock_, backlog) < 0)
            {
                std::cerr << "listen socket error" << std::endl;
                exit(LISTEN_ERR);
            }
        }

        void Start()
        {
            // 方法二：通过信号的方式让OS来回收子进程资源（推荐）
            //  signal(SIGCHLD, SIG_IGN); //SIGCHLD表示子进程退出时给父进程发送信号，SIG_IGN表示父进程忽略对子进程的操作

            // 完成从客户端接收信息，并且将信息返回给客户端
            quit_ = false;
            while (!quit_)
            {
                // 用于获取发送端的ip+端口号
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                memset(&client, 0, len);

                // 获取连接
                int sock = accept(listensock_, (struct sockaddr *)&client, &len); // 这个sock是用来与客户端进行通信的
                if (sock < 0)
                {
                    std::cerr << "accept socket err" << std::endl;
                    // 获取文件描述符失败则一直进行获取
                    continue;
                }

                // 提取客户端信息
                std::string clientip = inet_ntoa(client.sin_addr); // 将ip整数转字符串
                uint16_t clientport = ntohs(client.sin_port);

                // 获取新连接成功, 开始进行业务处理
                std::cout << "获取新连接成功: " << sock << " from " << listensock_ << ", "
                          << clientip << "-" << clientport << std::endl;

                // 业务处理：
                // v1：主线程只能完成一次连接
                // service(sock, clientip, clientport);

                // v2：多进程的版本
                //  pid_t id = fork();
                //  //子进程会拷贝父进程的数据，两份数据内容一样地址不一样
                //  if(id < 0)
                //  {
                //      //创建子进程失败
                //      close(sock);
                //      continue; //重新创建
                //  }
                //  else if(id == 0)
                //  {
                //      //child：完成消息的接收与发送
                //      close(listensock_); //关闭不必要的文件描述符

                //     //方法三：创建孙子进程，使孙子进程变成孤儿进程
                //     if(fork() > 0) exit(0);

                //     //到次行代码时，正在执行的是孙子进程
                //     service(sock, clientip, clientport);
                //     exit(0);
                // }

                // parent
                //  close(sock);
                // 等待子进程有多种方法：通过waitpdi，信号的方式，创建孙子进程（子进程退出让OS接管孙子进程）
                // 方法一：通过waitpid等待子进程：
                //  pid_t ret = waitpid(id, nullptr, WNOHANG); //非阻塞式等待，但是不推荐这个做法
                //  if(ret == id) std::cout << "wait child " << id << " success" << std::endl;

                // 多进程版本，可以很好的处理多执行流问题。但是，多进程会消耗大量的服务端资源

                // v3：多线程版本
                // pthread_t tid;
                // ThreadData* td = new ThreadData(sock, clientip, clientport, this); //传this是为了调用service函数
                // pthread_create(&tid, nullptr, threadRoutine, td);
                // //这里不能等待线程，不然又会造成阻塞状态

                //上面实现的多线程，是用户来了才创建子线程，效率不高，不是最佳版本

                //v4：线程池版本
            }
        }

        static void* threadRoutine(void* args)
        {
            //分离线程:主线程无需等待子线程
            pthread_detach(pthread_self());

            //类型转换
            ThreadData* td = static_cast<ThreadData*>(args);
            td->current->service(td->sock, td->clientip, td->clientport); //业务处理

            //资源释放
            delete td;
            return nullptr;
        }

        void service(int sock, const std::string &clientip, const uint16_t &clientport)
        {
            std::string who = clientip + "-" + std::to_string(clientport);
            char buffer[1024];
            // 从客户端获取数据
            while (true)
            {
                // 读取数据
                ssize_t n = read(sock, buffer, sizeof(buffer) - 1); // 文件中读取是不包含\0,要预留一个位置
                if (n > 0)
                {
                    // 读取成功
                    buffer[n] = '\0';
                    std::string res = func_(buffer); // 进行回调
                    std::cout << "message : " << res << std::endl;

                    // 将读取的数据返还给客户端
                    write(sock, res.c_str(), res.size());
                }
                else if (n == 0)
                {
                    // 读取到文件末尾，也就是对方将连接关闭了
                    close(sock); // 将sock文件描述符关闭
                    std::cout << who << " quit, me too" << std::endl;
                    break;
                }
                else
                {
                    // 读取错误
                    close(sock);
                    std::cerr << "read error: " << strerror(errno) << std::endl;
                    break;
                }
            }
        }

        ~TcpServer()
        {
        }

    private:
        int listensock_; // 套接字描述符
        uint16_t port_;  // 服务器端口号
        bool quit_;

        func_t func_; // 用于业务处理的回调
    };
}
